/*
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software Foundation,
 *    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package JET.VirtualKeyboard;

import JET.Actions.Action;
import JET.Centers.ControlCenter;
import JET.Scene;
import java.awt.event.KeyEvent;
import java.util.Hashtable;
import javax.script.ScriptException;
import org.w3c.dom.*;

/**
 *
 * @author 057Ry
 */
public class VKeyboard
{
    static final ControlCenter CC = ControlCenter.getInstance();
    private Hashtable vKeys;
    private Scene scene;

    public VKeyboard(Scene s)
    {
        this.scene = s;
        
        vKeys = new Hashtable(104);

        addKey(new Integer(KeyEvent.VK_ESCAPE), new VKey(KeyEvent.VK_ESCAPE, "ESCAPE") );
        addKey(new Integer(KeyEvent.VK_F1), new VKey(KeyEvent.VK_F1, "F1") );
        addKey(new Integer(KeyEvent.VK_F2), new VKey(KeyEvent.VK_F2, "F2") );
        addKey(new Integer(KeyEvent.VK_F3), new VKey(KeyEvent.VK_F3, "F3") );
        addKey(new Integer(KeyEvent.VK_F4), new VKey(KeyEvent.VK_F4, "F4") );
        addKey(new Integer(KeyEvent.VK_F5), new VKey(KeyEvent.VK_F5, "F5") );
        addKey(new Integer(KeyEvent.VK_F6), new VKey(KeyEvent.VK_F6, "F6") );
        addKey(new Integer(KeyEvent.VK_F7), new VKey(KeyEvent.VK_F7, "F7") );
        addKey(new Integer(KeyEvent.VK_F8), new VKey(KeyEvent.VK_F8, "F8") );
        addKey(new Integer(KeyEvent.VK_F9), new VKey(KeyEvent.VK_F9, "F9") );
        addKey(new Integer(KeyEvent.VK_F10), new VKey(KeyEvent.VK_F10, "F10") );
        addKey(new Integer(KeyEvent.VK_F11), new VKey(KeyEvent.VK_F11, "F11") );
        addKey(new Integer(KeyEvent.VK_F12), new VKey(KeyEvent.VK_F12, "F12") );
        addKey(new Integer(KeyEvent.VK_SCROLL_LOCK), new VKey(KeyEvent.VK_SCROLL_LOCK, "SCROLL_LOCK") );
        addKey(new Integer(KeyEvent.VK_PAUSE), new VKey(KeyEvent.VK_PAUSE, "PAUSE") );

        addKey(new Integer(KeyEvent.VK_BACK_QUOTE), new VKey(KeyEvent.VK_BACK_QUOTE, "TYLDA") );
        addKey(new Integer(KeyEvent.VK_1), new VKey(KeyEvent.VK_Q, "1") );
        addKey(new Integer(KeyEvent.VK_2), new VKey(KeyEvent.VK_Q, "2") );
        addKey(new Integer(KeyEvent.VK_3), new VKey(KeyEvent.VK_Q, "3") );
        addKey(new Integer(KeyEvent.VK_4), new VKey(KeyEvent.VK_Q, "4") );
        addKey(new Integer(KeyEvent.VK_5), new VKey(KeyEvent.VK_Q, "5") );
        addKey(new Integer(KeyEvent.VK_6), new VKey(KeyEvent.VK_Q, "6") );
        addKey(new Integer(KeyEvent.VK_7), new VKey(KeyEvent.VK_Q, "7") );
        addKey(new Integer(KeyEvent.VK_8), new VKey(KeyEvent.VK_Q, "8") );
        addKey(new Integer(KeyEvent.VK_9), new VKey(KeyEvent.VK_Q, "9") );
        addKey(new Integer(KeyEvent.VK_0), new VKey(KeyEvent.VK_Q, "9") );
        addKey(new Integer(KeyEvent.VK_MINUS), new VKey(KeyEvent.VK_MINUS, "MINUS") );
        addKey(new Integer(KeyEvent.VK_EQUALS), new VKey(KeyEvent.VK_EQUALS, "EQUALS") );
        addKey(new Integer(KeyEvent.VK_BACK_SPACE), new VKey(KeyEvent.VK_BACK_SPACE, "BACKSPACE") );

        addKey(new Integer(KeyEvent.VK_Q), new VKey(KeyEvent.VK_Q, "Q") );
        addKey(new Integer(KeyEvent.VK_W), new VKey(KeyEvent.VK_W, "W") );
        addKey(new Integer(KeyEvent.VK_E), new VKey(KeyEvent.VK_E, "E") );
        addKey(new Integer(KeyEvent.VK_R), new VKey(KeyEvent.VK_R, "R") );
        addKey(new Integer(KeyEvent.VK_T), new VKey(KeyEvent.VK_T, "T") );
        addKey(new Integer(KeyEvent.VK_Y), new VKey(KeyEvent.VK_Y, "Y") );
        addKey(new Integer(KeyEvent.VK_U), new VKey(KeyEvent.VK_U, "U") );
        addKey(new Integer(KeyEvent.VK_I), new VKey(KeyEvent.VK_I, "I") );
        addKey(new Integer(KeyEvent.VK_O), new VKey(KeyEvent.VK_O, "O") );
        addKey(new Integer(KeyEvent.VK_P), new VKey(KeyEvent.VK_P, "P") );
        addKey(new Integer(KeyEvent.VK_OPEN_BRACKET), new VKey(KeyEvent.VK_OPEN_BRACKET, "OPEN_BRACKET") );
        addKey(new Integer(KeyEvent.VK_CLOSE_BRACKET), new VKey(KeyEvent.VK_CLOSE_BRACKET, "CLOSE_BRACKET") );

        addKey(new Integer(KeyEvent.VK_A), new VKey(KeyEvent.VK_A, "A") );
        addKey(new Integer(KeyEvent.VK_S), new VKey(KeyEvent.VK_S, "S") );
        addKey(new Integer(KeyEvent.VK_D), new VKey(KeyEvent.VK_D, "D") );
        addKey(new Integer(KeyEvent.VK_F), new VKey(KeyEvent.VK_F, "F") );
        addKey(new Integer(KeyEvent.VK_G), new VKey(KeyEvent.VK_G, "G") );
        addKey(new Integer(KeyEvent.VK_H), new VKey(KeyEvent.VK_H, "H") );
        addKey(new Integer(KeyEvent.VK_J), new VKey(KeyEvent.VK_J, "J") );
        addKey(new Integer(KeyEvent.VK_K), new VKey(KeyEvent.VK_K, "K") );
        addKey(new Integer(KeyEvent.VK_L), new VKey(KeyEvent.VK_L, "L") );
        addKey(new Integer(KeyEvent.VK_SEMICOLON), new VKey(KeyEvent.VK_SEMICOLON, "SEMICOLON") );
        addKey(new Integer(KeyEvent.VK_QUOTE), new VKey(KeyEvent.VK_QUOTE, "QUOTE") );
        addKey(new Integer(KeyEvent.VK_ENTER), new VKey(KeyEvent.VK_ENTER, "ENTER") );

        addKey(new Integer(KeyEvent.VK_SHIFT), new VKey(KeyEvent.VK_SHIFT, "SHIFT") );
        addKey(new Integer(KeyEvent.VK_Z), new VKey(KeyEvent.VK_Z, "Z") );
        addKey(new Integer(KeyEvent.VK_X), new VKey(KeyEvent.VK_X, "X") );
        addKey(new Integer(KeyEvent.VK_C), new VKey(KeyEvent.VK_C, "C") );
        addKey(new Integer(KeyEvent.VK_V), new VKey(KeyEvent.VK_V, "V") );
        addKey(new Integer(KeyEvent.VK_B), new VKey(KeyEvent.VK_B, "B") );
        addKey(new Integer(KeyEvent.VK_N), new VKey(KeyEvent.VK_N, "N") );
        addKey(new Integer(KeyEvent.VK_M), new VKey(KeyEvent.VK_M, "M") );
        addKey(new Integer(KeyEvent.VK_COMMA), new VKey(KeyEvent.VK_COMMA, "COMMA") );
        addKey(new Integer(KeyEvent.VK_PERIOD), new VKey(KeyEvent.VK_PERIOD, "PERIOD") );
        addKey(new Integer(KeyEvent.VK_SLASH), new VKey(KeyEvent.VK_SLASH, "SLASH") );

        addKey(new Integer(KeyEvent.VK_CONTROL), new VKey(KeyEvent.VK_CONTROL, "CONTROL") );
        addKey(new Integer(KeyEvent.VK_WINDOWS), new VKey(KeyEvent.VK_WINDOWS, "WINDOWS") );
        addKey(new Integer(KeyEvent.VK_ALT), new VKey(KeyEvent.VK_ALT, "ALT") );
        addKey(new Integer(KeyEvent.VK_SPACE), new VKey(KeyEvent.VK_SPACE, "SPACE") );
        addKey(new Integer(KeyEvent.VK_CONTEXT_MENU), new VKey(KeyEvent.VK_CONTEXT_MENU, "CONTEXT_MENU") );

        addKey(new Integer(KeyEvent.VK_UP), new VKey(KeyEvent.VK_UP, "UP") );
        addKey(new Integer(KeyEvent.VK_DOWN), new VKey(KeyEvent.VK_DOWN, "DOWN") );
        addKey(new Integer(KeyEvent.VK_LEFT), new VKey(KeyEvent.VK_LEFT, "LEFT") );
        addKey(new Integer(KeyEvent.VK_RIGHT), new VKey(KeyEvent.VK_RIGHT, "RIGHT") );

        addKey(new Integer(KeyEvent.VK_INSERT), new VKey(KeyEvent.VK_INSERT, "INSERT") );
        addKey(new Integer(KeyEvent.VK_HOME), new VKey(KeyEvent.VK_HOME, "HOME") );
        addKey(new Integer(KeyEvent.VK_PAGE_UP), new VKey(KeyEvent.VK_PAGE_UP, "PAGE_UP") );
        addKey(new Integer(KeyEvent.VK_PAGE_DOWN), new VKey(KeyEvent.VK_PAGE_DOWN, "PAG_DOWN") );
        addKey(new Integer(KeyEvent.VK_DELETE), new VKey(KeyEvent.VK_DELETE, "DElETE") );
        addKey(new Integer(KeyEvent.VK_END), new VKey(KeyEvent.VK_END, "END") );

        addKey(new Integer(KeyEvent.VK_NUM_LOCK), new VKey(KeyEvent.VK_NUM_LOCK, "NUM_LOCK") );
        addKey(new Integer(KeyEvent.VK_NUMPAD0), new VKey(KeyEvent.VK_NUMPAD0, "NUMPAD0") );
        addKey(new Integer(KeyEvent.VK_NUMPAD1), new VKey(KeyEvent.VK_NUMPAD1, "NUMPAD1") );
        addKey(new Integer(KeyEvent.VK_NUMPAD2), new VKey(KeyEvent.VK_NUMPAD2, "NUMPAD2") );
        addKey(new Integer(KeyEvent.VK_NUMPAD3), new VKey(KeyEvent.VK_NUMPAD3, "NUMPAD3") );
        addKey(new Integer(KeyEvent.VK_NUMPAD4), new VKey(KeyEvent.VK_NUMPAD4, "NUMPAD4") );
        addKey(new Integer(KeyEvent.VK_NUMPAD5), new VKey(KeyEvent.VK_NUMPAD5, "NUMPAD5") );
        addKey(new Integer(KeyEvent.VK_NUMPAD6), new VKey(KeyEvent.VK_NUMPAD6, "NUMPAD6") );
        addKey(new Integer(KeyEvent.VK_NUMPAD7), new VKey(KeyEvent.VK_NUMPAD7, "NUMPAD7") );
        addKey(new Integer(KeyEvent.VK_NUMPAD8), new VKey(KeyEvent.VK_NUMPAD8, "NUMPAD8") );
        addKey(new Integer(KeyEvent.VK_NUMPAD9), new VKey(KeyEvent.VK_NUMPAD9, "NUMPAD9") );
        
        addKey(new Integer(111), new VKey(111, "NUMPAD SLASH") );
        addKey(new Integer(106), new VKey(106, "NUMPAD STAR") );
        addKey(new Integer(109), new VKey(109, "NUMPAD MINUS") );
        addKey(new Integer(107), new VKey(107, "NUMPAD PLUS") );

        // When numlock isnt on, numpad 5 is CLEAR key
        addKey(new Integer(KeyEvent.VK_CLEAR), new VKey(KeyEvent.VK_CLEAR, "CLEAR") );

        // Template to ctrl+v method :P
        //addKey(new Integer(KeyEvent.VK_), new VKey(KeyEvent.VK_, "") );
    }

    public void addKey(Integer keyValue, VKey virtualKey) {
        vKeys.put(keyValue, virtualKey);
    }

    /**
     * Returns key of specified code.
     * Key codes are stored in static fields of java.awt.event.KeyEvent class.
     *
     * @param keyCode Code of key to find
     * @return Virtual key object.
     */
    public VKey getKey(int keyCode) {
        return (VKey) vKeys.get(new Integer(keyCode));
    }

    /**
     * Returns key by its name.
     *
     * @param keyCode Code of key to find
     * @return Virtual key object.
     */
    public VKey getKey(String name) {
        for(Object o : vKeys.values()) {
            VKey key = (VKey) o;
            if (key.toString().equalsIgnoreCase(name))
                return key;
        }
        return null;
    }

    public void keyPressed(int keyCode, double simulationTime)
    {
        VKey key = getKey(keyCode);
        if (key != null && !key.isPressed()) {
            key.setPressReleaseMoment(simulationTime);
            key.setPressed(true);
            key.callPressActions();

            // Notify JavaScript of this key press
            try {
                scene.SC.getJsEngine().eval("JetSystem.callOnKeyDownListeners('" + key + "', " + keyCode + ")");
            } catch (ScriptException ex) {
                CC.GUIC.printlnToOutput("[Ex] Exception while notifying onKeyDown listeners. [key: "+key+"]");
                ex.printStackTrace();
            }
        }
    }

    public void keyReleased(int keyCode, double simulationTime)
    {
        VKey key = getKey(keyCode);
        if (key != null) {
            key.setPressReleaseMoment(simulationTime);
            key.setPressed(false);
            key.callReleaseActions();

            // Notify JavaScript of this key release
            try {
                scene.SC.getJsEngine().eval("JetSystem.callOnKeyUpListeners('" + key + "', " + keyCode + ")");
            } catch (ScriptException ex) {
                CC.GUIC.printlnToOutput("[Ex] Exception while notifying onKeyUp listeners. [key: "+key+"]");
                ex.printStackTrace();
            }
        }
    }

//    public Element getXMLRepresentation(Document doc, ControlCenter cc)
//    {
//        Element rootElement = doc.createElement("Virtual_Keyboard");
//
//        for(Object o : vKeys.values())
//        {
//            VKey vk = (VKey)o;
//            if (!vk.getPressActions().isEmpty() || !vk.getReleaseActions().isEmpty())
//            {
//                Element vkEl = doc.createElement("Vkey_Actions");
//                vkEl.setAttribute("name", ""+vk.toString());
//
//                Element vkCodeEl = doc.createElement("VKey_Code");
//                vkCodeEl.appendChild(doc.createTextNode(""+vk.getCode()));
//                vkEl.appendChild(vkCodeEl);
//
//                if (!vk.getPressActions().isEmpty())
//                {
//                    Element vkPressActionsEl = doc.createElement("Press_Actions_Indices");
//                    for(Action a : vk.getPressActions())
//                    {
//                        Element actionIndexEl = doc.createElement("Action_Index");
//                        actionIndexEl.appendChild(doc.createTextNode(""+cc.AC.getActionList().indexOf(a)));
//                        vkPressActionsEl.appendChild(actionIndexEl);
//                    }
//                    vkEl.appendChild(vkPressActionsEl);
//                }
//
//                if (!vk.getReleaseActions().isEmpty())
//                {
//                    Element vkReleaseActionsEl = doc.createElement("Release_Actions_Indices");
//                    for(Action a : vk.getReleaseActions())
//                    {
//                        Element actionIndexEl = doc.createElement("Action_Index");
//                        actionIndexEl.appendChild(doc.createTextNode(""+cc.AC.getActionList().indexOf(a)));
//                        vkReleaseActionsEl.appendChild(actionIndexEl);
//                    }
//                    vkEl.appendChild(vkReleaseActionsEl);
//                }
//
//                rootElement.appendChild(vkEl);
//            }
//        }
//
//        return rootElement;
//    }
}
